﻿namespace ScrumLite.Services
{
    using System;
    using System.Collections.ObjectModel;
    using System.Transactions;
    using Contracts;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;

    public class LinqReviewsService : IReviewsService
    {
        ScrumLite.Data.IReviewStore store;

        public LinqReviewsService()
            : this(new ScrumLite.Data.ReviewStore())
        {
        }

        public LinqReviewsService(ScrumLite.Data.IReviewStore store)
        {
            this.store = store;
        }

        public Review[] GetReviews(string projectId)
        {
            return Map(this.store.RetrieveReviews(new Guid(projectId)));
        }

        public Review GetReview(string reviewId)
        {
            return Map(this.store.RetrieveReview(new Guid(reviewId)));
        }

        public void AddReview(Review review)
        {
            review.Id = Guid.NewGuid();            
            ScrumLite.Data.Review data = Map(review);

            this.store.Add(data);        
        }

        public void UpdateReview(Review review)
        {
            ScrumLite.Data.Review data = Map(review);            
            this.store.Update(data);            
        }

        private static Review[] Map(ScrumLite.Data.Review[] input)
        {
            List<Review> output = new List<Review>();
            foreach (ScrumLite.Data.Review item in input)
            {
                output.Add(Map(item));
            }

            return output.ToArray();
        }

        internal static Review Map(ScrumLite.Data.Review input)
        {
            if (input == null)
                return null;

            Review output = new Review()
            {
                Id = input.id,
                ProjectId = input.fk_project,
                OccurredOn = input.occuredOn,
                ConductedBy = input.conductedBy,
                EvidenceUrl = input.uri_evidence,
            };

            return output;
        }

        internal static ScrumLite.Data.Review Map(Review input)
        {
            if (input == null)
                return null;

            ScrumLite.Data.Review output = new ScrumLite.Data.Review()
            {
                id = input.Id,
                fk_project = input.ProjectId,
                conductedBy = input.ConductedBy,
                occuredOn = input.OccurredOn,
                uri_evidence = input.EvidenceUrl
            };

            return output;
        }

        public Dictionary<int, int> GetReviewsByWeek(string projectId)
        {
            var query = (from Daily in this.store.RetrieveDailyReviewCount(new Guid(projectId))
                         group Daily by GetYearWeek(Daily.Key) into Weekly
                         select new { Week = Weekly.Key, Count = Weekly.Sum(r => r.Value) });
            return query.ToDictionary(k => k.Week, v => v.Count);
        }

        private static int GetYearWeek(DateTime date)
        {
            CultureInfo invariant = CultureInfo.InvariantCulture;
            return invariant.Calendar.GetWeekOfYear(date,
                                   invariant.DateTimeFormat.CalendarWeekRule,
                                   invariant.DateTimeFormat.FirstDayOfWeek);
        }
    }
}
